package net.lab0.tools.evo;

import java.util.ArrayList;
import java.util.List;

import net.lab0.tools.StaticRandom;
import net.lab0.tools.evo.iface.Individual;

/**
 * 
 * @author 116@lab0.net
 * 
 *         A standard evolution algorithm example. The genome is an array of booleans. The objective will be to have an
 *         individual with as many <code>true</code> genes as possible. Crossing genomes assumes that the two
 *         individuals genomes have the same length.
 * 
 */
public class BinaryIndividual
implements Individual<BinaryIndividual>
{
    private boolean[] genome;
    private double    fitness;
    
    /**
     * Creates a new BinaryIndividual with a randomly initialised genome.
     * 
     * @param size
     *            the size of the genome. Must be >0.
     * 
     * @throws BinaryIndividual
     *             if the size is incorrect.
     */
    public BinaryIndividual(int size)
    {
        if (size <= 0)
        {
            throw new IllegalArgumentException("A genome's size must be positive.");
        }
        genome = new boolean[size];
        for (int i = 0; i < size; ++i)
        {
            genome[i] = StaticRandom.random.nextBoolean();
        }
        computeFitness();
    }
    
    /**
     * Creates a binary individual with the given genome.
     * 
     * @param genome
     */
    public BinaryIndividual(boolean[] genome)
    {
        this.genome = genome;
        computeFitness();
    }
    
    public void mutate()
    {
        int i = StaticRandom.random.nextInt(genome.length);
        genome[i] = !genome[i];
        computeFitness();
    }
    
    @Override
    public List<BinaryIndividual> cross(BinaryIndividual other)
    {
        BinaryIndividual individual = (BinaryIndividual) other;
        int splitPoint = StaticRandom.random.nextInt(this.genome.length);
        
        boolean[] newGenome1 = new boolean[this.genome.length];
        boolean[] newGenome2 = new boolean[this.genome.length];
        
        for (int i = 0; i < splitPoint; ++i)
        {
            newGenome1[i] = this.genome[i];
            newGenome2[i] = individual.genome[i];
        }
        for (int i = splitPoint; i < this.genome.length; ++i)
        {
            newGenome1[i] = individual.genome[i];
            newGenome2[i] = this.genome[i];
        }
        
        List<BinaryIndividual> ret = new ArrayList<>(2);
        ret.add(new BinaryIndividual(newGenome1));
        ret.add(new BinaryIndividual(newGenome2));
        
        return ret;
    }
    
    @Override
    public double getFitness()
    {
        return fitness;
    }
    
    private void computeFitness()
    {
        int trues = 0;
        for (int i = 0; i < this.genome.length; ++i)
        {
            if (genome[i])
            {
                trues++;
            }
        }
        fitness = (double) trues;
    }
}
